/**
 * 
 */
package models;

import java.util.Calendar;

/**
 * @author LAP
 *
 */
public class UpdateDataGenerator {
	
	private final int HALF_OF_SEC = 500;
	private final int FIVE_MIN_IN_SEC = 310;
	private final int DAY_IN_MIN = 1440;
	private final int FIVE_DAYS_IN_HAURS = 120;
	private final int SIX_MONTH_IN_DAYS = 180;
	private final int FIVE_YEARS_IN_MONTH = 60;
	
	private Calendar finalDate;
	
	
	private int zoom; 
	
	private double zoomMaximum;
	private double shadowZoomMaximum;
	private double position;
	
	private int[] zoomConstants = {HALF_OF_SEC, FIVE_MIN_IN_SEC, DAY_IN_MIN, FIVE_DAYS_IN_HAURS, SIX_MONTH_IN_DAYS, FIVE_YEARS_IN_MONTH};
	private int[] zoomValues = {Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR, Calendar.DAY_OF_YEAR, Calendar.MONTH, Calendar.YEAR};
	
	/**
	 * 
	 * @param finalDate
	 */
	public UpdateDataGenerator(Calendar finalDate) {
		if (finalDate == null) throw new NullPointerException("Final date must be not null");
		this.finalDate = finalDate;
		zoom = -1;
		zoomMaximum = 0;
		position = 0;
	}
	public void generate(Calendar checkDate) throws UpdateProgressException {
		if (checkDate.after(finalDate)) throw new UpdateProgressException("Check date must be befor final date");
		setZoom(checkDate);
		setPosition(checkDate);
	}
	/**
	 * 
	 * @param checkDate
	 * @return
	 */
	private void setPosition(Calendar checkDate) {
		if (checkDate == null) throw new NullPointerException("Checked date must be not null");
		Calendar difference = Calendar.getInstance();
		difference.setTimeInMillis(finalDate.getTimeInMillis() - checkDate.getTimeInMillis());
		switch(zoom) {
		case Calendar.SECOND: position = zoomMaximum - (difference.getTimeInMillis() / 1000); break;
		case Calendar.MINUTE: position = zoomMaximum - (difference.getTimeInMillis() / (1000 * 60)); break;
		case Calendar.HOUR: position = zoomMaximum - (difference.getTimeInMillis() / (1000 * 60 * 60)); break;
		case Calendar.DAY_OF_YEAR: position = zoomMaximum - (difference.getTimeInMillis() / (1000 * 60 * 60 * 24)); break;
		case Calendar.MONTH: position = zoomMaximum - getMonthCount(checkDate); break;
		case Calendar.YEAR: position = finalDate.get(Calendar.YEAR) - checkDate.get(Calendar.YEAR); break;
		}
	}
	
	private void setZoom(Calendar checkDate) {
		if (checkDate == null) throw new NullPointerException("Checked date must be not null");
		shadowZoomMaximum = (finalDate.getTimeInMillis() - checkDate.getTimeInMillis());
		int shadowZoom = 0;
		for(int i = 0; i < zoomValues.length; i++) {
			if (isInMaxZoom(zoomConstants[i], checkDate))
				break;
			else shadowZoom = zoomValues[i];
		}
		if (zoom != shadowZoom) {
			zoomMaximum = shadowZoomMaximum;
			zoom = shadowZoom;
		}
	}
	
	private boolean isInMaxZoom(int zoomConstant, Calendar checkCalendar) {
		if (shadowZoomMaximum > zoomConstant) {
			switch(zoomConstant) {
			case HALF_OF_SEC: shadowZoomMaximum /= 1000;
				return false;
			case FIVE_MIN_IN_SEC:
			case DAY_IN_MIN: shadowZoomMaximum /= 60;
				return false;
			case FIVE_DAYS_IN_HAURS: shadowZoomMaximum /= 24;
				return false;
			case SIX_MONTH_IN_DAYS: shadowZoomMaximum = (double) getMonthCount(checkCalendar);
				return false;
			case FIVE_YEARS_IN_MONTH: shadowZoomMaximum /= 12;
				return false;
			}
		}
		return true;
	}
	
	private int getMonthCount(Calendar checkCalendar) {
		Calendar inst = (Calendar)checkCalendar.clone();
		int monthCount = 12 - inst.get(Calendar.MONTH);
		while(inst.get(Calendar.YEAR) <= finalDate.get(Calendar.YEAR)) {
			monthCount += 12;
			inst.add(Calendar.YEAR, 1);
		}
		return monthCount;
	}
	/**
	 * @return the zoomMaximum
	 */
	public int getZoomMaximum() {
		return (int)Math.round(zoomMaximum);
	}
	/**
	 * @return the position
	 */
	public int getPosition() {
		return (int)Math.round(position);
	}
}
